/*
 * Copyright (c) 2011, Marc-Andre Ferland - madrang@gmail.com
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the The Warrent Team nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *     * This software is free for non-commercial use. You may not use this
 *       software, in whole or in part, in support of any commercial product
 *       without the express consent of the author.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE WARRENT TEAM BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
using System;
using System.Runtime.InteropServices;

namespace OsHelper.UsbNet.Descriptors
{
	/// <summary>
	/// A structure representing the standard USB device descriptor. 
	/// This descriptor is documented in section 9.6.1 of the USB 2.0 specification. 
	/// </summary>
	[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 18)]
	public class DeviceDescriptor
	{
		/// <summary>
		/// Total size of this structure in bytes.
		/// </summary>
		public static int Size {
			get { return Marshal.SizeOf(typeof (DeviceDescriptor)); }
		}
		
		[FieldOffsetAttribute(0)]
		private Descriptor BaseDesc;
		
		[FieldOffsetAttribute(2)]
		private BcdVersion bcd_Specification;
		
		/// <summary>
		/// Class Code (Assigned by USB Org)
		/// If equal to Zero, each interface specifies it’s own class code; If equal to 0xFF, the class code is vendor specified; Otherwise field is valid Class Code.
		/// </summary>
		[FieldOffsetAttribute(4)]
		public ClassCodeType Class;
		
		/// <summary>
		/// Subclass Code (Assigned by USB Org)
		/// </summary>
		[FieldOffsetAttribute(5)]
		public byte SubClass;

		/// <summary>
		/// Protocol Code (Assigned by USB Org)
		/// </summary>
		[FieldOffsetAttribute(6)]
		public byte Protocol;

		/// <summary>
		/// Maximum Packet Size for Zero Endpoint. Valid Sizes are 8, 16, 32, 64
		/// </summary>
		[FieldOffsetAttribute(7)]
		public byte MaxPacketSize;

		/// <summary>
		/// Vendor ID (Assigned by USB Org)
		/// </summary>
		[FieldOffsetAttribute(8)]
		public ushort VendorID;

		/// <summary>
		/// Product ID (Assigned by Manufacturer)
		/// </summary>
		[FieldOffsetAttribute(10)]
		public ushort ProductID;
		
		[FieldOffsetAttribute(12)]
		private BcdVersion bcd_DeviceRelease;
		
		/// <summary>
		/// Index of Manufacturer String Descriptor
		/// </summary>
		[FieldOffsetAttribute(14)]
		public byte ManufacturerStringIndex;

		/// <summary>
		/// Index of Product String Descriptor
		/// </summary>
		[FieldOffsetAttribute(15)]
		public byte ProductStringIndex;

		/// <summary>
		/// Index of Serial Number String Descriptor
		/// </summary>
		[FieldOffsetAttribute(16)]
		public byte SerialStringIndex;

		/// <summary>
		/// Number of Possible Configurations
		/// </summary>
		[FieldOffsetAttribute(17)]
		public byte ConfigurationCount;
		
		public DeviceDescriptor () : base()
		{
			this.Length = (byte)DeviceDescriptor.Size;
			this.Type = new DescriptorType(StandardDescriptorSubType.Device);
		}
		
		public byte[] ToBytes()
		{
			return Serialyser.RawSerialize(this);
		}
		
		public static DeviceDescriptor FromBytes(byte[] Data)
		{
			if(Data == null)
				throw new ArgumentNullException("Data");
			
			if(Data.Length < DeviceDescriptor.Size)
				throw new ArgumentException(string.Format("Data.Length: {0}, DeviceDescriptor.Size: {1}", Data.Length, DeviceDescriptor.Size));
			
			return (DeviceDescriptor)OsHelper.Serialyser.RawDeserialize(Data, typeof(DeviceDescriptor));
		}
		
		///<summary>
		///Returns a <see cref="System.String"/> that represents the current <see cref="DeviceDescriptor"/>.
		///</summary>
		///
		///<returns>
		///A <see cref="System.String"/> that represents the current <see cref="DeviceDescriptor"/>.
		///</returns>
		public override string ToString ()
		{
			return string.Format ("Type: {0}, Specification Number: {1}, Class Code: {2} {3}, ID: {4}:{5}, Device Release: {6}",
			                      this.Type,
			                      this.Specification,
			                      this.Class,
			                      this.SubClass,
			                      this.VendorID.ToString ("X4"),
			                      this.ProductID.ToString ("X4"),
			                      this.DeviceRelease);
		}
		
		/// <summary>
		/// USB Specification Number which device complies too.
		/// </summary>
		public Version Specification {
			get { return (Version)this.bcd_Specification; }
			set { this.bcd_Specification = ((BcdVersion)value); }
		}

		/// <summary>
		/// Device Release Number
		/// </summary>
		public Version DeviceRelease {
			get { return (Version)this.bcd_DeviceRelease; }
			set { this.bcd_DeviceRelease = ((BcdVersion)value); }
		}
		
		public byte Length {
			get { return this.BaseDesc.Length; }
			private set { this.BaseDesc.Length = value; }
		}
		
		public UsbNet.Descriptors.DescriptorType Type {
			get { return this.BaseDesc.Type; }
			private set { this.BaseDesc.Type = value; }
		}
		
	}
}
